package com.sojson.util.job;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;

import com.sojson.enums.core.EGeneralYesNo;
import com.sojson.project.monitor.job.entity.vo.JobVo;
import com.sojson.util.ExpUtil;
import com.sojson.util.job.impl.QuartzDisallowConcurrentExecution;
import com.sojson.util.job.impl.QuartzJobExecution;

/**
 * 定时任务工具类
 * 
 * @author liu
 * @date 2021-04-20
 */
public class ScheduleUtil {

    /**
     * 得到quartz任务类
     *
     * @param job 执行计划
     * @return 具体执行任务类
     */
    private static Class<? extends Job> getQuartzJobClass(JobVo job) {
        return EGeneralYesNo.YES.getCode() == job.getConcurrent() ? QuartzJobExecution.class
            : QuartzDisallowConcurrentExecution.class;
    }

    /**
     * 构建任务触发对象
     */
    public static TriggerKey getTriggerKey(String id, String group) {
        return TriggerKey.triggerKey(ScheduleConstant.TASK_CLASS_NAME + id, group);
    }

    /**
     * 构建任务键对象
     */
    public static JobKey getJobKey(String id, String group) {
        return JobKey.jobKey(ScheduleConstant.TASK_CLASS_NAME + id, group);
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, JobVo job) throws Exception {
        Class<? extends Job> jobClass = getQuartzJobClass(job);
        // 构建job信息
        String id = job.getId();
        String group = job.getGroup();
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(id, group)).build();

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(id, group))
            .withSchedule(cronScheduleBuilder).build();

        // 放入参数，运行时的方法可以获取
        jobDetail.getJobDataMap().put(ScheduleConstant.TASK_PROPERTIES, job);

        // 判断是否存在
        if (scheduler.checkExists(getJobKey(id, group))) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(getJobKey(id, group));
        }

        scheduler.scheduleJob(jobDetail, trigger);

        // 暂停任务
        if (job.getStatus() == ScheduleConstant.Status.PAUSE.getValue()) {
            scheduler.pauseJob(ScheduleUtil.getJobKey(id, group));
        }
    }

    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(JobVo job, CronScheduleBuilder cb) {
        switch (job.getMisfirePolicy()) {
            case ScheduleConstant.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstant.MISFIRE_IGNORE_MISFIRES:
                // 将错过的全部执行一次,再按照正常的Cron频率依次执行
                // 以错过的第一个频率时间立刻开始执行
                // 重做错过的所有频率周期后
                // 当下一次触发频率发生时间大于当前时间后，再按照正常的Cron频率依次执行
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstant.MISFIRE_FIRE_AND_PROCEED:
                // 将上一次错过的立即执行一次,再按照正常的Cron频率依次执行
                // 以当前时间为触发频率立刻触发一次执行
                // 然后按照Cron频率依次执行
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConstant.MISFIRE_DO_NOTHING:
                // 错过的放弃执行,再按照正常的Cron频率依次执行
                // 不触发立即执行
                // 等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                ExpUtil.throwEx("The task misfire policy '" + job.getMisfirePolicy()
                    + "' cannot be used in cron schedule tasks" + Code.CONFIG_ERROR);
                return null;
        }
    }

    public enum Code {
        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
    }

}